// OpentTxl-C Version 11 safe strings
// Copyright 2023, James R. Cordy and others

// All strings in OpenTxl are 1-origin and chars are [1 .. length]

#include <string.h>
#include <ctype.h>

// OpenTxl logic assumes these limits
#define maxStringLength         4095    // 4 kb - 1; limits TXL source code lines
#define maxLongStringLength     1048575 // 1 Mb - 1; limits target language source code lines

// All strings in OpenTxl's source code must be declared using the types "string" or "longstring"
typedef char string[maxStringLength + 1];
typedef char longstring[maxLongStringLength + 1];

// String length
#ifdef CHECKED
    extern int stringlen (const string src);
    extern int lstringlen (const longstring src);
#else 
    #define stringlen(stringsrc)                        (int) strlen(stringsrc)
    #define lstringlen(lstringsrc)                      (int) strlen(lstringsrc)
#endif 

// String comparison - -1 for <, 0 for =, 1 for >
#ifdef CHECKED
    #define stringcmp(stringsrc1,stringsrc2)            strncmp(stringsrc1, stringsrc2, maxStringLength)
    #define lstringcmp(lstringsrc1,lstringsrc2)         strncmp(lstringsrc1, lstringsrc2, maxLongStringLength)
#else 
    #define stringcmp(stringsrc1,stringsrc2)            strcmp(stringsrc1, stringsrc2)
    #define lstringcmp(lstringsrc1,lstringsrc2)         strcmp(lstringsrc1, lstringsrc2)
#endif
#define stringncmp(stringsrc1,stringsrc2,length)        memcmp(stringsrc1, stringsrc2, length)
#define lstringncmp(lstringsrc1,lstringsrc2,length)     memcmp(lstringsrc1, lstringsrc2, length)

// Safe string copy
#ifdef CHECKED
    extern void stringcpy (string dest, const string src);
    extern void lstringcpy (longstring dest, const longstring src);
#else
    #define stringcpy(stringdest,stringsrc)             strcpy(stringdest, stringsrc)
    #define lstringcpy(lstringdest,lstringsrc)          strcpy(lstringdest, lstringsrc)
#endif

// Safe string concatenation
#ifdef CHECKED
    extern void stringcat (string dest, const string src);
    extern void lstringcat (longstring dest, const longstring src);
#else
    #define stringcat(stringdest,stringsrc)             strcat(stringdest, stringsrc)
    #define lstringcat(lstringdest,lstringsrc)          strcat(lstringdest, lstringsrc)
#endif

// Safe sprintf 
#ifdef CHECKED
    #define stringprintf(dest, ...)                     snprintf (dest, maxStringLength, __VA_ARGS__) 
    #define lstringprintf(dest, ...)                    snprintf (dest, maxLongStringLength, __VA_ARGS__) 
#else
    #define stringprintf(dest, ...)                     sprintf (dest, __VA_ARGS__) 
    #define lstringprintf(dest, ...)                    sprintf (dest, __VA_ARGS__) 
#endif

// Safe int-to-string and string-to-int, bases 10 and 16
extern void intstring (int value, int width, int base, string target);
extern int stringint_answer;
extern double stringreal_answer;
#define lintstring(intval, width, base, longdest) intstring (intval, width, base, longdest)
#define stringint(src) (sscanf (src, "%i", &stringint_answer), stringint_answer)
#define lstringint(longsrc) stringint (longsrc)

// Safe real-to-string and string-to-real
#define realstring(realval, width, dest) snprintf(dest, maxStringLength, "%.15g", realval)
#define lrealstring(realval, width, dest) snprintf(dest, maxLongStringLength, "%.15g", realval)
#define stringreal(src) (sscanf (src, "%le", &stringreal_answer), stringreal_answer)
#define lstringreal(longsrc) stringreal (longsrc)

// Extended string operations

// String repeat - concatenate several copies of a string
extern void stringrep (string dest, const string src, const int n);
extern void lstringrep (longstring dest, const longstring src, const int n);

// Substring
extern void substring (string dest, const string src, const int lower, const int upper);
extern void lsubstring (longstring dest, const longstring src, const int lower, const int upper);

// String character - 1-origin 
#define stringchar(src, index)  ((src)[(index) - 1])
#define lstringchar(src, index) ((src)[(index) - 1])

// String index - find the position of the first instance of a string in a string
extern int stringindex (const string src, const string pat);
extern int lstringindex (const longstring src, const longstring pat);

// Upper-to-lower and lower-to-upper case string maps 
extern void stringtolower (string srcdest); 
extern void stringtoupper (string srcdest);
extern void lstringtolower (longstring srcdest); 
extern void lstringtoupper (longstring srcdest);

#ifdef CHECKED
    #ifndef IO
        // Undefine dangerous C string library so that we don't accidentally use it
        #undef strcpy
        #undef strcat
        #undef sprintf
        #define strcpy(dest,src)    UNSAFE
        #define strcat(dest,src)    UNSAFE
        #define sprintf(dest,src)   UNSAFE
    #endif
#endif
